package cn.miju.wechatpay.handler;

import cn.miju.wechatpay.auth.CustomCertificatesManager;
import cn.miju.wechatpay.auth.CustomWechatPay2Credentials;
import cn.miju.wechatpay.auth.CustomWechatPayHttpClientBuilder;
import cn.miju.wechatpay.properties.WechatpayProperties;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <pre>
 *
 * 文件名 :  AbstractWechatpayHandler
 * 创建时间 : 2023年03月13日 14:37
 * 文件描述 : 无
 *
 * </pre>
 *
 * @author Huzb
 * @version 1.0.0
 */
public abstract class AbstractWechatpayHandler<REQ, RESP> {

    protected static final Integer SUCCESS_CODE = 200;
    protected static final Integer SUCCESS_CODE_NO_RESPONSE = 204;
    private static final Logger log = LoggerFactory.getLogger(AbstractWechatpayHandler.class);

    /**
     * 新知识点：final所修饰的基本类型值不可变，但是修饰的引用类型是指址不变，所以引用类型只是不能被new，可以CRUD
     */
    private static final ConcurrentHashMap<String, CloseableHttpClient> HTTPCLIENT_MAP = new ConcurrentHashMap<>();

    /**
     * 请求参数前置处理
     *
     * @param req 请求参数
     * @return 结果
     */
    protected abstract String beforeRequest(REQ req);

    /**
     * 核心执行
     *
     * @param req 请求参数
     * @return 结果
     */
    public abstract RESP doPlan(REQ req);

    /**
     * 响应参数后置处理
     *
     * @param result 响应报文
     * @param statusCode 响应状态码
     * @return 结果
     */
    protected abstract RESP afterResponse(String result, int statusCode);


    /**
     * 获取微信支付客户端
     *
     * @param properties WechatpayProperties
     * @return 微信请求客户端
     */
    protected CloseableHttpClient getWechatpayClient(WechatpayProperties properties) {
        CloseableHttpClient httpClient = HTTPCLIENT_MAP.get(properties.getMchId());
        if (httpClient == null) {
            try {
                httpClient = initWechatpayClient(properties);
                HTTPCLIENT_MAP.put(properties.getMchId(), httpClient);
            } catch (Exception e) {
                log.error("API--创建微信客户端异常-->Exception：{}", e.getMessage(), e);
                // todo： 这里先用RuntimeException进行抛出，后续看情况修改
                throw new RuntimeException(e);
            }
        }
        return httpClient;
    }

    /**
     * 创建微信请求客户端
     *
     * @param properties WechatpayProperties
     * @return 微信请求客户端
     * @throws GeneralSecurityException GeneralSecurityException
     * @throws IOException              IOException
     * @throws HttpCodeException        HttpCodeException
     * @throws NotFoundException        NotFoundException
     */
    private CloseableHttpClient initWechatpayClient(WechatpayProperties properties) throws GeneralSecurityException,
            IOException, HttpCodeException, NotFoundException {

        // 商户私钥
        PrivateKey privateKey = PemUtil.loadPrivateKey(properties.getPrivateKey());
//        X509Certificate certificate = PemUtil.loadCertificate(null);
//        String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();

        // 证书管理器实例
        CustomCertificatesManager certificatesManager = CustomCertificatesManager.getInstance(
                properties.getUrl() + "/v3/certificates");

        // 向证书管理器添加需要自动更新证书的商户信息
        certificatesManager.putMerchant(properties.getMchId(),
                new CustomWechatPay2Credentials(properties.getMchId(), new PrivateKeySigner(properties.getMchSerialNo(), privateKey), properties.getUrl()),
                properties.getV3Secret().getBytes(StandardCharsets.UTF_8));

        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(properties.getMchId());

        // 构建http
        CustomWechatPayHttpClientBuilder wechatPayHttpClientBuilder = CustomWechatPayHttpClientBuilder.create()
                // todo: 验证解析出来的证书序列号和后台保存的是否一致
                .withMerchant(properties.getMchId(), properties.getMchSerialNo(), privateKey, properties.getUrl())
                .withValidator(new WechatPay2Validator(verifier));

        return wechatPayHttpClientBuilder.build();
    }


    protected HttpPost getHttpPost(String url, StringEntity stringEntity) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(stringEntity);
        return httpPost;
    }

    protected HttpGet getHttpGet(String url) {
        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader("Accept", "application/json");
        return httpGet;
    }

    protected String requsetAddOptParams(JSONObject jsonObject, Map<String, String> optionalParams) {
        if (optionalParams != null && !optionalParams.isEmpty()) {
            jsonObject.forEach((k, v) -> optionalParams.remove(k));
            jsonObject.putAll(optionalParams);
        }
        return JSON.toJSONString(jsonObject);
    }
}
